﻿using CPPEI.ETL.DataServiceInterface;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace CPPEI.ETL.DSImplement
{
    public static class WSTypeUtil
    {
        public static string GetFullName(string prefix, string name)
        {
            if(!string.IsNullOrEmpty(prefix))
            {
                return string.Format("{0}.{1}", prefix, name);
            }
            return name;
        }

        #region validate result type

        /// <summary>
        /// 判断是否是支持的结果类型
        /// </summary>
        /// <param name="resultType"></param>
        /// <returns></returns>
        public static bool IsSupportResultType(Type resultType)
        {
            if (resultType == typeof(DataSet)
                || resultType == typeof(DataTable)
                || resultType == typeof(XmlNode)
                || resultType == typeof(XmlElement))
            {
                return true;
            }
            else if (resultType == typeof(string) || !resultType.IsClass)
            {
                // 字符串和值类型
                return true;
            }
            else
            {
                string rootPath;
                Type elementType;
                if(IsArrayObjectResult(string.Empty, resultType, out rootPath, out elementType))
                {
                    return true;
                }

                if(IsSingleObjectResult(resultType))
                {
                    return true;
                }
                return false;
            }
        }

        #endregion

        /// <summary>
        /// 将返回结果的转换为列信息（把返回结果对象转换成可以插入表格的结构）
        /// </summary>
        /// <param name="tSource"></param>
        /// <param name="prefix"></param>
        /// <param name="returnValueType"></param>
        public static void ConvertToColumns(TDataSource tSource, string prefix, Type returnValueType)
        {
            List<string> names = new List<string>();
            List<Type> types = new List<Type>();
            GetColumns(prefix, returnValueType, names, types);

            for (int index = 0; index < names.Count; index++)
            {
                TDataSourceColumn column = new TDataSourceColumn(names[index], types[index]);

                tSource.Columns.Add(column);
            }
        }

        public static void ConvertToParameters(TDataSource tSource, string prefix, string name, Type parameterType)
        {
            if (parameterType != typeof(string) && parameterType.IsClass)
            {
                if(parameterType.IsArray)
                {
                    // 参数里如果有数组，怎提示不支持
                    throw new WS.WSNotSupportException();
                }

                FieldInfo[] fields = parameterType.GetFields();
                foreach (var field in fields)
                {
                    ConvertToParameters(tSource, GetFullName(prefix, name), field.Name, field.FieldType);
                }
            }
            else
            {
                TDataSourceParameter dtParameter = new TDataSourceParameter(GetFullName(prefix, name), parameterType);
                tSource.Parameters.Add(dtParameter);
            }
        }

        #region v2 for property

        public static void GetColumns(string prefix, Type returnValueType, List<string> names, List<Type> types)
        {
            if(returnValueType == typeof(XmlElement)
                || returnValueType == typeof(XmlNode))
            {
                return;
            }
            else
            {
                GetColumns4Object(false, prefix, returnValueType, names, types);
            }
        }

        public static void GetColumns4Object(string prefix, object returnValue, List<string> names, List<Type> types)
        {
            if (returnValue is XmlElement
                || returnValue is XmlNode)
            {
                GetColumns4XmlNode(prefix, (XmlNode)returnValue, names, types);
            }
        }

        /// <summary>
        /// 将返回结果的信息解析成数据列。
        /// 返回结果是一个自定义数据类型，然后便利便利类型里的字段得到可以插入到数据表的列信息，如果类型中含有不规则的数据对象字段
        /// 将会出错。
        /// </summary>
        /// <param name="hasArrayParent"></param>
        /// <param name="prefix"></param>
        /// <param name="returnValueType"></param>
        /// <param name="names">解析到的名称</param>
        /// <param name="types">解析到的类型</param>
        /// <exception cref="WS.WSNotSupportException"></exception>
        private static void GetColumns4Object(bool hasArrayParent, string prefix, Type returnValueType,  List<string> names, List<Type> types)
        {
            if (returnValueType != typeof(string) && returnValueType.IsClass)
            {
                // 复杂数据类型
                if (returnValueType.IsArray)
                {
                    if (hasArrayParent)
                    {
                        // 数组对象里的子元素又含有数组
                        throw new WS.WSNotSupportException();
                    }
                    else
                    {
                        hasArrayParent = true;
                    }
                    // 返回结果是一个数组
                    Type elementType = returnValueType.GetElementType();
                    GetColumns4Object(hasArrayParent, prefix, elementType, names, types);
                }
                else
                {
                    FieldInfo[] fields = returnValueType.GetFields();

                    // check fields，there only can have one array fields
                    foreach (var field in fields)
                    {
                        if (field.FieldType.IsArray && fields.Length > 1)
                        {
                            // 返回结果里有属性是数组，同时还有属性不是数组
                            throw new WS.WSNotSupportException();
                        }
                    }

                    foreach (var field in fields)
                    {
                        GetColumns4Object(hasArrayParent, GetFullName(prefix, field.Name), field.FieldType, names, types);
                    }
                }
            }
            else
            {
                // 简单数据类型
                names.Add(prefix);
                types.Add(returnValueType);
            }
        }

        private static void GetColumns4XmlNode(string prefix, XmlNode xNode, List<string> names, List<Type> types)
        {
            if(xNode.ChildNodes == null || xNode.ChildNodes.Count == 0)
            {
                // 数组对象里的子元素又含有数组
                throw new WS.WSNotSupportException();
            }
            
            if(xNode.ChildNodes.Count == 1)
            {
                // 只有一个子节点，视为只有一个子节点的数组
                XmlNode xChildNode = xNode.ChildNodes[0];
                if(xChildNode.ChildNodes == null || xChildNode.ChildNodes.Count == 0)
                {
                    names.Add(xChildNode.Name);
                    types.Add(typeof(string));
                }
                else
                {
                    GetColumns4XmlNode(GetFullName(prefix, xChildNode.Name), xChildNode, names, types);
                }
            }
            else
            {
                // 如果所有子节点的name都一样，那么是数组，否则就是一个单一对象
                string childElementName = null;
                bool allChildIsSame = true;
                bool allChildIsDiffrent = true;
                for (int index = 0; index < xNode.ChildNodes.Count; index++)
                {
                    if(childElementName == null)
                    {
                        childElementName = xNode.ChildNodes[index].Name; 
                    }
                    else if(string.Equals(childElementName, xNode.ChildNodes[index].Name))
                    {
                        allChildIsSame = false; 
                    }
                    else
                    {
                        allChildIsDiffrent = false;
                    }
                }
                if(allChildIsSame)
                {
                    // 含有一样的子节点
                    // 继续向下便利
                    GetColumns4XmlNode(GetFullName(prefix, childElementName), xNode.ChildNodes[0], names, types);
                }
                else if(allChildIsDiffrent)
                {
                    // 所有子节点都不一样
                    for (int index = 0; index < xNode.ChildNodes.Count; index++)
                    {
                        names.Add(xNode.ChildNodes[index].Name);
                        types.Add(typeof(string));
                    }
                }
                else
                {
                    throw new WS.WSNotSupportException();
                }
            }
        }

        /// <summary>
        /// 判断一个对象类型是否是对象数组类型,或在这个属性结构数中只存在一个数组结构
        /// </summary>
        /// <param name="prefix"></param>
        /// <param name="returnValueType"></param>
        /// <param name="rootPath"></param>
        /// <returns></returns>
        public static bool IsArrayObjectResult(string prefix, Type returnValueType,out string rootPath, out Type elementType)
        {
            if(_isArrayObjectResult(false, prefix, returnValueType, out rootPath, out elementType, new List<Type>()))
            {
                return true;
            }

            if (_isContainsOnlyoneArrayObjectResult(false, prefix, returnValueType, out rootPath, out elementType, new List<Type>()))
            {
                return true;
            }
            rootPath = string.Empty;
            elementType = null;
            return false;
        }
        /// <summary>
        /// 内部包含了一个数组
        /// </summary>
        /// <param name="hasArrayParent"></param>
        /// <param name="prefix"></param>
        /// <param name="returnValueType"></param>
        /// <param name="rootPath"></param>
        /// <param name="elementType"></param>
        /// <param name="foundTypeList"></param>
        /// <returns></returns>
        private static bool _isContainsOnlyoneArrayObjectResult(bool hasArrayParent, string prefix, Type returnValueType, out string rootPath, out Type elementType, List<Type> foundTypeList)
        {
            if(returnValueType.IsArray)
            {
                if(_isContainsAnyArrayObjectResult(returnValueType.GetElementType(), new List<Type>()))
                {
                    rootPath = string.Empty;
                    elementType = null;
                    return false;
                }
                rootPath = prefix;
                elementType = returnValueType.GetElementType();
                return true;
            }
            else if (returnValueType != typeof(string) && returnValueType.IsClass)
            {
                // 获取数组类型的属性
                FieldInfo[] fields = returnValueType.GetFields();
                List<FieldInfo> arrFields = fields.Where(p => p.FieldType.IsArray).ToList();
                List<FieldInfo> nonArrFields = fields.Where(p => p.FieldType.IsArray == false).ToList();

                if (arrFields.Count == 1)
                {
                    hasArrayParent = true;
                    #region // 检查是否是在非数组类型字段里还含有数组
                    bool hasSubArrayFieldInNonArrayField = false;
                    for (int index = 0; index < nonArrFields.Count; index++)
                    {
                        if (_isContainsAnyArrayObjectResult(nonArrFields[index].FieldType, new List<Type>()))
                        {
                            hasSubArrayFieldInNonArrayField = true;
                            break;
                        }
                    }
                    if (hasSubArrayFieldInNonArrayField)
                    {
                        elementType = null;
                        rootPath = string.Empty;
                        return false;
                    }
                    #endregion
                    return _isContainsOnlyoneArrayObjectResult(hasArrayParent, GetFullName(prefix, arrFields[0].Name), arrFields[0].FieldType, out rootPath, out elementType, foundTypeList);
                }
                else if (arrFields.Count == 0)
                {
                    elementType = null;
                    rootPath = string.Empty;
                    // 循环看子属性里有没有数组格式
                    int subArrayObjectCount = 0;
                    for (int index = 0; index < fields.Length; index++)
                    {
                        string tempRootPath;
                        Type tempElementType;
                        if (_isContainsOnlyoneArrayObjectResult(hasArrayParent, GetFullName(prefix, fields[index].Name), fields[index].FieldType, out tempRootPath, out tempElementType, foundTypeList))
                        {
                            rootPath = tempRootPath;
                            elementType = tempElementType;
                            subArrayObjectCount++;
                        }
                    }

                    if (subArrayObjectCount == 1)
                    {
                        return true;
                    }
                }
                // 含有多个数据属性或没有数组结构，不合格
                elementType = null;
                rootPath = string.Empty;
                return false;
            }
            elementType = null;
            rootPath = string.Empty;
            return false;
        }

        /// <summary>
        /// 类型中是否含有数组结构，一个或多个都是true，没有就是false
        /// </summary>
        /// <param name="returnValueType"></param>
        /// <param name="foundTypeList"></param>
        /// <returns></returns>
        private static bool _isContainsAnyArrayObjectResult(Type returnValueType, List<Type> foundTypeList)
        {
            if (returnValueType != typeof(string) && returnValueType.IsClass)
            {
                // 获取数组类型的属性
                FieldInfo[] fields = returnValueType.GetFields();
                List<FieldInfo> arrFields = fields.Where(p => p.FieldType.IsArray).ToList();
                List<FieldInfo> nonArrFields = fields.Where(p => p.FieldType.IsArray == false).ToList();

                if (arrFields.Count >0)
                {
                    return true;
                }
                else
                {
                    for (int index = 0; index < nonArrFields.Count; index++)
                    {
                        if(foundTypeList.Contains(nonArrFields[index].FieldType))
                        {
                            continue;
                        }
                        foundTypeList.Add(nonArrFields[index].FieldType);
                        if (_isContainsAnyArrayObjectResult(nonArrFields[index].FieldType, foundTypeList))
                        {
                            return true;
                        }
                    }
                    return false;
                }
            }
            return false;
        }
        
        /// <summary>
        /// 本身就是一个数组
        /// </summary>
        /// <param name="hasArrayParent"></param>
        /// <param name="prefix"></param>
        /// <param name="returnValueType"></param>
        /// <param name="rootPath"></param>
        /// <param name="elementType"></param>
        /// <param name="foundTypeList"></param>
        /// <returns></returns>
        private static bool _isArrayObjectResult(bool hasArrayParent, string prefix, Type returnValueType, out string rootPath, out Type elementType, List<Type> foundTypeList)
        {
            if (returnValueType.IsArray)
            {
                elementType = returnValueType.GetElementType();
                rootPath = prefix;

                if (_isContainsAnyArrayObjectResult(elementType, new List<Type>()))
                {
                    rootPath = string.Empty;
                    elementType = null;
                    return false;
                }
                return true;
            }
            else
            {
                rootPath = string.Empty;
                elementType = null;
                return false;
            }
        }

        /// <summary>
        /// 判断一个类型是否是简单对象，对象里不含有数组
        /// </summary>
        /// <param name="returnValueType"></param>
        /// <returns></returns>
        public static bool IsSingleObjectResult(Type returnValueType)
        {
            List<Type> foundTypeList = new List<Type>();
            return _isSingleObjectResult(returnValueType, foundTypeList);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="returnValueType"></param>
        /// <param name="foundTypeList">已经查找过的类型</param>
        /// <returns></returns>
        private static bool _isSingleObjectResult(Type returnValueType, List<Type> foundTypeList)
        {
            if (returnValueType.IsArray)
            {
                return false;
            }

            FieldInfo[] fields = returnValueType.GetFields();
            List<FieldInfo> arrFields = fields.Where(p => p.FieldType.IsArray).ToList();
            if (arrFields.Count > 0)
            {
                return false;
            }
            foreach (var field in fields)
            {
                if(foundTypeList.Contains(field.FieldType))
                {
                    // 重复出现了以前出现过的类型，说明有循环的引用关系，可能是一种树型或环形链表的数据结构，跳过该类型
                    continue;
                }

                foundTypeList.Add(field.FieldType);
                if (!_isSingleObjectResult(field.FieldType, foundTypeList))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 获取对象的某路径的值
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="path">字段路径，格式（RESULT.???.???）</param>
        /// <returns></returns>
        public static object GetFieldValue(object obj, string path)
        {
            string[] pathSegments = path.Split(new char[] { '.' });

            if (pathSegments.Length < 1)
            {
                return null;
            }
            else if(pathSegments.Length == 1)
            {
                // 该对象自己就是数据
                return obj;
            }

            return GetFieldValue(obj, pathSegments, 1);
        }

        private static object GetFieldValue(object obj, string[] path, int pathIndex)
        {
            FieldInfo[] fields = obj.GetType().GetFields();
            for (int index = 0; index < fields.Length; index++)
            {
                if (string.Equals(fields[index].Name, path[pathIndex]))
                {
                    object fieldValue = fields[index].GetValue(obj);

                    if (pathIndex == (path.Length - 1))
                    {
                        //  已经到结束了
                        return fieldValue;
                    }
                    else
                    {
                        return GetFieldValue(fieldValue, path, pathIndex + 1);
                    }
                }
            }
            return null;
        }
        #endregion

        public class XmlNodeUtil
        {
            public static bool Parse(string prefix, XmlNode xNode, out string rootPath, out List<string> names, out List<Type> types, out int totalCount)
            {
                names = new List<string>();
                types = new List<Type>();
                rootPath = string.Empty;
                totalCount = 0;

                return GetColumnsAndCount(GetFullName(prefix, xNode.Name), xNode, ref rootPath, names, types, ref totalCount);
            }

            private static bool GetColumnsAndCount(string prefix, XmlNode xNode,  ref string rootXPath, List<string> names, List<Type> types, ref int totalCount)
            {
                if (xNode.ChildNodes == null || xNode.ChildNodes.Count == 0)
                {
                    totalCount = 0;
                    return false;
                }

                if (xNode.ChildNodes.Count == 1)
                {
                    // 只有一个子节点，视为只有一个子节点的数组
                    XmlNode xChildNode = xNode.ChildNodes[0];
                    if (xChildNode.ChildNodes == null || xChildNode.ChildNodes.Count == 0)
                    {
                        if (!string.IsNullOrEmpty(rootXPath))
                        {
                            rootXPath = string.Format("{0}/{1}", rootXPath, xChildNode.Name);
                        }
                        else
                        {
                            rootXPath = ".";
                        }

                        names.Add(GetFullName(prefix, xChildNode.Name));
                        types.Add(typeof(string));
                        totalCount = 1;
                        return true;
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(rootXPath))
                        {
                            rootXPath = string.Format("{0}/{1}", rootXPath, xChildNode.Name);
                        }
                        else
                        {
                            rootXPath = ".";
                        }
                        return GetColumnsAndCount(GetFullName(prefix, xChildNode.Name), xChildNode, ref rootXPath, names, types, ref totalCount);
                    }
                }
                else
                {
                    // 如果所有子节点的name都一样，那么是数组，否则就是一个单一对象
                    string childElementName = null;
                    bool allChildIsSame = true;
                    bool allChildIsDiffrent = true;

                    // 循环2个就行
                    for (int index = 0; index < 2; index++)
                    {
                        if (childElementName == null)
                        {
                            childElementName = xNode.ChildNodes[index].Name;
                        }
                        else if (string.Equals(childElementName, xNode.ChildNodes[index].Name))
                        {
                            allChildIsDiffrent = false;
                        }
                        else
                        {
                            allChildIsSame = false;
                        }
                    }

                    if (allChildIsSame)
                    {
                        // 含有一样的子节点
                        // 继续向下遍历
                        totalCount = xNode.ChildNodes.Count;
                        if (!string.IsNullOrEmpty(rootXPath))
                        {
                            rootXPath = string.Format("{0}/{1}", rootXPath, childElementName);
                        }
                        else
                        {
                            rootXPath = string.Format("./{0}", childElementName);
                        }

                        return GetColumnsAndCount(GetFullName(prefix, childElementName), xNode.ChildNodes[0], ref rootXPath, names, types, ref totalCount);
                    }
                    else if (allChildIsDiffrent)
                    {
                        // 所有子节点都不一样
                        for (int index = 0; index < xNode.ChildNodes.Count; index++)
                        {
                            names.Add(GetFullName(prefix, xNode.ChildNodes[index].Name));
                            types.Add(typeof(string));
                        }
                        return true;
                    }
                    else
                    {
                        totalCount = 0;
                        return false;
                    }
                }
            }

            public static string GetValue(XmlNode xNode, string xRelativePath)
            {
                XmlNode xValueNode = xNode.SelectSingleNode(xRelativePath);
                if(xValueNode!=null)
                {
                    return xValueNode.InnerText;
                }
                return null;
            }

            public static string GetXPath(XmlNode xNode)
            {
                return GetXPathReverse(xNode, string.Empty);
            }

            /// <summary>
            /// 逆向获得xpath
            /// </summary>
            /// <param name="xNode"></param>
            /// <param name="relativePath"></param>
            /// <returns></returns>
            private static string GetXPathReverse(XmlNode xNode, string relativePath)
            {
                if(string.IsNullOrEmpty(relativePath))
                {
                    if (xNode.ParentNode != null)
                    {
                        StringBuilder builder = new StringBuilder();
                        builder.AppendFormat("{0}/", xNode.Name);
                        return GetXPathReverse(xNode.ParentNode, builder.ToString());
                    }
                    else
                    {
                        return string.Format("{0}/", xNode.Name);
                    }
                }
                else
                {
                    if (xNode.ParentNode != null)
                    {
                        StringBuilder builder = new StringBuilder();
                        builder.AppendFormat("{0}/{1}", xNode.Name, relativePath);
                        return GetXPathReverse(xNode.ParentNode, builder.ToString());
                    }
                    else
                    {
                        return string.Format("{0}/{1}", xNode.Name, relativePath);
                    }
                }
            }
        }
    }
}
